package com.york.jobmew.circle.server.sensitive;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.york.jobmew.circle.api.enums.IsDeletedFlagEnum;
import com.york.jobmew.circle.server.config.thread.CustomNameThreadFactory;
import com.york.jobmew.circle.server.entity.po.SensitiveWords;
import com.york.jobmew.circle.server.service.SensitiveWordsService;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 词库上下文环境
 * <p>
 * 初始化敏感词库，将敏感词加入到HashMap中，构建DFA算法模型
 * <p>
 * 在配置类中已经注册该bean，被注入到容器中
 */
@SuppressWarnings({"rawtypes", "unchecked"})
@Slf4j
public class WordContext {

    /**
     * 敏感词字典
     * 用于存储DFA模型的根节点
     */
    @Getter
    private final Map wordMap = new HashMap(1024);

    /**
     * 是否已初始化
     */
    private boolean init;

    /**
     * 记录最近加载的敏感词ID，用于增量加载新词
     */
    private long addLastId;

    /**
     * 构造函数
     *
     * @param autoLoad 是否自动加载敏感词库
     * @param service  敏感词服务
     */
    public WordContext(boolean autoLoad, SensitiveWordsService service) {
        // 清理已删除的敏感词
        clearDelData(service);
        // 黑名单（敏感词）
        Set<String> black = new HashSet<>();
        // 白名单（非敏感词）
        Set<String> white = new HashSet<>();

        // 从数据库加载未删除的敏感词
        List<SensitiveWords> list = service.list(Wrappers.<SensitiveWords>lambdaQuery()
                .eq(SensitiveWords::getIsDeleted, IsDeletedFlagEnum.UN_DELETED.getCode()));

        // 将敏感词按类型分类：黑名单和白名单
        for (SensitiveWords words : list) {
            if (words.getType() == 1) {
                black.add(words.getWords());
            } else {
                white.add(words.getWords());
            }
        }

        // 记录最新加载的敏感词ID
        if (CollectionUtils.isNotEmpty(list)) {
            this.addLastId = list.get(list.size() - 1).getId();
        }

        // 初始化敏感词库
        initKeyWord(black, white);

        // 是否开启自动加载
        if (autoLoad) {
            // 开启定时任务，动态加载新词
            reloadWord(service);
        }
    }

    /**
     * 清理已删除的敏感词
     *
     * @param service 敏感词服务
     */
    private void clearDelData(SensitiveWordsService service) {
        LambdaUpdateWrapper<SensitiveWords> remove = Wrappers.<SensitiveWords>lambdaUpdate()
                .eq(SensitiveWords::getIsDeleted, IsDeletedFlagEnum.DELETED.getCode());
        service.remove(remove);
    }

    /**
     * 定时任务：动态加载新词和删除已标记的敏感词
     * 交由线程池-sensitiveWordsThreadPool处理
     *
     * @param service 敏感词服务
     */
    private void reloadWord(SensitiveWordsService service) {
        // 创建一个单线程的定时线程池
        ScheduledExecutorService scheduler =
                Executors.newSingleThreadScheduledExecutor(new CustomNameThreadFactory("sensitiveWords"));

        // 创建一个Runnable任务
        Runnable task = () -> {
            try {
                // 加载新增的敏感词
                addNewWords(service);
                // 移除已删除的敏感词
                removeDelWords(service);
                log.info("Sensitive words refreshed successfully");
            } catch (Exception e) {
                log.error("Sensitive words task error", e);
            }
        };

        // 定时执行任务：初始延迟0，之后每分钟执行一次
        scheduler.scheduleAtFixedRate(task, 0, 1, TimeUnit.MINUTES);
    }

    /**
     * 移除已标记为删除的敏感词
     *
     * @param service 敏感词服务
     */
    private void removeDelWords(SensitiveWordsService service) {
        LambdaUpdateWrapper<SensitiveWords> query = Wrappers.<SensitiveWords>lambdaUpdate()
                .eq(SensitiveWords::getIsDeleted, IsDeletedFlagEnum.DELETED.getCode());
        // 查询已删除的敏感词
        List<SensitiveWords> list = service.list(query);

        if (CollectionUtils.isNotEmpty(list)) {
            log.info("removeDelWords {}", JSON.toJSON(list));
            Set<String> black = new HashSet<>();
            Set<String> white = new HashSet<>();

            // 将已删除的敏感词按类型分类
            for (SensitiveWords words : list) {
                if (words.getType() == 1) {
                    black.add(words.getWords());
                } else {
                    white.add(words.getWords());
                }
            }

            // 从DFA模型中移除敏感词
            removeWord(black, WordType.BLACK);
            removeWord(white, WordType.WHITE);
        }
    }

    /**
     * 加载新增的敏感词
     *
     * @param service 敏感词服务
     */
    private void addNewWords(SensitiveWordsService service) {
        // 查询ID大于addLastId的敏感词
        LambdaUpdateWrapper<SensitiveWords> query = Wrappers.<SensitiveWords>lambdaUpdate()
                .gt(SensitiveWords::getId, addLastId)
                .eq(SensitiveWords::getIsDeleted, IsDeletedFlagEnum.UN_DELETED.getCode());
        List<SensitiveWords> list = service.list(query);

        if (CollectionUtils.isNotEmpty(list)) {
            log.info("addNewWords {}", JSON.toJSON(list));
            this.addLastId = list.get(list.size() - 1).getId();
            Set<String> black = new HashSet<>();
            Set<String> white = new HashSet<>();

            // 将新增的敏感词按类型分类
            for (SensitiveWords words : list) {
                if (words.getType() == 1) {
                    black.add(words.getWords());
                } else {
                    white.add(words.getWords());
                }
            }

            // 将新增的敏感词添加到DFA模型中
            addWord(black, WordType.BLACK);
            addWord(white, WordType.WHITE);
        }
    }

    /**
     * 初始化敏感词库
     *
     * @param black 黑名单
     * @param white 白名单
     */
    private synchronized void initKeyWord(Set<String> black, Set<String> white) {
        try {
            if (!init) {
                // 将黑名单和白名单添加到DFA模型中
                addWord(black, WordType.BLACK);
                addWord(white, WordType.WHITE);
            }
            // 标记已初始化
            init = true;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 将词条添加到DFA模型中
     *
     * @param wordList 词条列表
     * @param wordType 词条类型（黑名单或白名单）
     */
    public void addWord(Collection<String> wordList, WordType wordType) {
        if (CollectionUtils.isEmpty(wordList)) {
            return;
        }
// 当前字符对应的Map
        Map nowMap;
        Map<String, String> newWorMap;

        // 遍历每个词条
        for (String key : wordList) {
            // 从根节点开始
            nowMap = wordMap;

            // 遍历词条的每个字符
            for (int i = 0; i < key.length(); i++) {
                char keyChar = key.charAt(i);
                // 查找当前字符是否已存在
                Object wordMap = nowMap.get(keyChar);

                // 如果存在，直接使用现有的Map
                if (wordMap != null) {
                    nowMap = (Map) wordMap;
                } else {
                    // 不存在则构建一个新的Map
                    newWorMap = new HashMap<>(4);
                    newWorMap.put("isEnd", String.valueOf(EndType.HAS_NEXT.ordinal()));
                    nowMap.put(keyChar, newWorMap);
                    nowMap = newWorMap;
                }

                // 如果是最后一个字符，标记结束
                if (i == key.length() - 1) {
                    nowMap.put("isEnd", String.valueOf(EndType.IS_END.ordinal()));
                    nowMap.put("isWhiteWord", String.valueOf(wordType.ordinal()));
                }
            }
        }
    }

    /**
     * 从DFA模型中移除词条
     *
     * @param wordList 词条列表
     * @param wordType 词条类型（黑名单或白名单）
     */
    public void removeWord(Collection<String> wordList, WordType wordType) {
        if (CollectionUtils.isEmpty(wordList)) {
            return;
        }

        Map nowMap;
        for (String key : wordList) {
            // 缓存遍历路径
            List<Map> cacheList = new ArrayList<>();
            nowMap = wordMap;

            // 遍历词条的每个字符
            for (int i = 0; i < key.length(); i++) {
                char keyChar = key.charAt(i);
                Object map = nowMap.get(keyChar);

                if (map != null) {
                    nowMap = (Map) map;
                    cacheList.add(nowMap);
                } else {
                    return;
                }

                // 如果是最后一个字符
                if (i == key.length() - 1) {
                    char[] keys = key.toCharArray();
                    boolean cleanable = false;
                    char lastChar = 0;

                    // 从最后一个字符开始向上清理
                    for (int j = cacheList.size() - 1; j >= 0; j--) {
                        Map cacheMap = cacheList.get(j);

                        // 如果是最后一个字符
                        if (j == cacheList.size() - 1) {
                            // 检查词条类型是否匹配
                            if (String.valueOf(WordType.BLACK.ordinal()).equals(cacheMap.get("isWhiteWord"))) {
                                if (wordType == WordType.WHITE) {
                                    return;
                                }
                            }
                            if (String.valueOf(WordType.WHITE.ordinal()).equals(cacheMap.get("isWhiteWord"))) {
                                if (wordType == WordType.BLACK) {
                                    return;
                                }
                            }

                            // 移除标记
                            cacheMap.remove("isWhiteWord");
                            cacheMap.remove("isEnd");

                            // 如果当前Map为空，标记需要清理
                            if (cacheMap.size() == 0) {
                                cleanable = true;
                                continue;
                            }
                        }

                        // 如果需要清理，移除上一个字符
                        if (cleanable) {
                            Object isEnd = cacheMap.get("isEnd");
                            if (String.valueOf(EndType.IS_END.ordinal()).equals(isEnd)) {
                                cleanable = false;
                            }
                            cacheMap.remove(lastChar);
                        }

                        lastChar = keys[j];
                    }

                    // 如果需要清理，移除根节点中的字符
                    if (cleanable) {
                        wordMap.remove(lastChar);
                    }
                }
            }
        }
    }
}